// ----------------------------------------------------------------------------
// serialize_compound_type.cpp
// ----------------------------------------------------------------------------
// TODO : アロケータの差し替え.
#include <fw/base/serialize/serialize_compound_type.h>
#include <fw/base/serialize/serialize_schema.h>
#include <fw/base/serialize/serialize_scalar_type.h>
#include <fw/base/serialize/serialize_scalar_value.h>
#include <fw/base/serialize/serialize_status.h>

#include <fw/base/serialize/serialize_instance_member.h>

#include <fw/base/serialize/byte_reader.h>
#include <fw/base/serialize/byte_writer.h>
#include <fw/base/core/assertion.h>

#include <stdio.h>
#include <cstring>


namespace fw
{
	const SerializeMember* SerializeCompoundType::FindMember(Hash memberName) const
	{
		return m_dictionary.FindByName(memberName);
	}
	
	SerializeStatus SerializeCompoundType::ConvertOne(char*						dstData,
													  const char*				srcData,
													  const SerializeType*		srcType,
													  class SerializeAllocator&	alloc,
													  int						arrayCount) const
	{
		FW_ASSERT(srcType);
		const SerializeCompoundType* srcCompoundType = srcType->AsCompoundType();
		FW_ASSERT(srcCompoundType);
		
		SerializeStatus status = kStatus_Ok;
		for(int dic_i=0; dic_i<m_dictionary.GetCount(); ++dic_i)
		{
			const SerializeMember* dstMember = m_dictionary.GetByIndex    (dic_i); FW_ASSERT(dstMember);
			Hash dstMemberName               = m_dictionary.GetNameByIndex(dic_i);
			
			const SerializeMember* srcMember = srcCompoundType->FindMember(dstMemberName);
			dstMember->Convert(dstData, srcData, *srcMember, alloc);
		}
		return status;
	}
	
	bool SerializeCompoundType::IsTerminal(const char *data, const SerializeScalarValue &terminatorValue, Hash terminatorName) const
	{
		const SerializeMember* member = FindMember(terminatorName);
		FW_ASSERT(member);
		return member->ReadScalarValue(data) == terminatorValue;
	}
	
	SerializeStatus SerializeCompoundType::FixUp(SerializeSchema &schema)
	{
		SerializeStatus status = kStatus_Ok;
		
		if(m_state == kState_Ready)
		{
			return status;
		}
		FW_ASSERT(m_state == kState_Defined);
		
		m_state = kState_Fixing;
		
		size_t alignment = 1;
		const SerializeMember* previousMember = nullptr;
		int count = m_dictionary.GetCount();
		FW_ASSERT(count, "Empty member.");
		for(int dic_i=0; dic_i<count; ++dic_i)
		{
			SerializeMember* member = m_dictionary.GetByIndex(dic_i);
			FW_ASSERT(member);
			status = member->FixUp(schema, previousMember);
			
			if(status != kStatus_Ok)
			{
				break;
			}
			
			size_t memberAlignment = 0;
			memberAlignment = member->GetAlignment();
			alignment = (memberAlignment > alignment) ? memberAlignment : alignment;
			
			previousMember = member;
		}
		size_t size = previousMember->GetOffset() + previousMember->GetTotalSize();
		
		m_size      = FW_ALIGNMENT(size, alignment);
		m_alignment = alignment;
		
		m_state = kState_Ready;
		
		return status;
	}
	
	void SerializeCompoundType::AddMember(Hash memberName, SerializeMember* member)
	{
		member->SetScope(this);
		m_dictionary.Add(memberName, member);
	}
	
	void SerializeCompoundType::AddInstance(Hash memberName, int count, Hash typeName)
	{
		AddMember(memberName, new SerializeInstanceMember(count, typeName));
	}
	
	void SerializeCompoundType::AddPointer(Hash memberName, int count, Hash typeName)
	{
		AddCountedPointer(memberName, count, typeName, Hash());
	}
	
	void SerializeCompoundType::AddCountedPointer(Hash memberName, int count, Hash typeName, Hash countName)
	{
		
	}
	
	void SerializeCompoundType::AddStringPointer(Hash							memberName,
												 int							count,
												 Hash							typeName,
												 Hash							terminatorName,
												 const SerializeScalarValue&	terminatorValue)
	{
		
	}
	
	SerializeCompoundType::~SerializeCompoundType()
	{
		for(int dic_i=0; dic_i<m_dictionary.GetCount(); ++dic_i)
		{
			delete m_dictionary.GetByIndex(dic_i);
		}
	}
	
	void SerializeCompoundType::Write(ByteWriter& writer) const
	{
		writer.Write8(kByteCode_Compound);
		
		int count = m_dictionary.GetCount();
		writer.Write16(count);
		
		for(int dic_i=0; dic_i<count; ++dic_i)
		{
			const SerializeMember* member = m_dictionary.GetByIndex(dic_i);
			Hash memberName = m_dictionary.GetNameByIndex(dic_i);
			writer.Write32(memberName);
			writer.Write  (member);
		}
	}
	
	SerializeCompoundType* SerializeCompoundType::ReadNew(ByteReader& reader)
	{
		size_t roolBack = reader.Tell();
		SerializeCompoundType* compound = nullptr;
		
		ByteCode code = static_cast<ByteCode>(reader.Read8());
		
		switch(code)
		{
		case SerializeType::kByteCode_Compound:
			{
				compound  = new SerializeCompoundType();
				int count = reader.Read16();
				
				for(int i=0; i<count; ++i)
				{
					Hash memberName = reader.Read32();
					SerializeMember* member = reader.ReadNew<SerializeMember>();
					if(member)
					{
						compound->AddMember(memberName, member);
					}
					else
					{	// Could not read member.
						delete compound;
						compound = nullptr;
						break;
					}
				}
			}
			break;
		default:
			break;
		}
		
		if(!compound)
		{
			reader.Seek(roolBack);
		}
		return compound;
	}
	
	uint64_t SerializeCompoundType::GetChecksum(uint64_t basis) const
	{
		int count = m_dictionary.GetCount();
		for(int dic_i=0; dic_i<count; ++dic_i)
		{
			const SerializeMember* member = m_dictionary.GetByIndex(dic_i);
			FW_ASSERT(member);
			
			Hash name = m_dictionary.GetNameByIndex(dic_i);
			basis = HashValue64((uint32_t)name, basis);
			basis = member->GetChecksum(basis);
		}
		return basis;
	}
	
} // namespace fw
